'''
1、环境搭建说明：测试模块为OPEN的版本需要sample_sensor在data目录下，并赋予可执行权限
   需要设置项：设置模块的debug口和AT口
   开启QDR，执行AT+QDRTEST=1,1,1，重启后，如当前版本GNSS并非自启动，则需手动启动GNSS                
2、脚本运行逻辑：
        1.关注sample_sensor是否正常运行，是否有数据输出，检查输出的数据是否正常
        2.获取CPU占用率,内存占用率,观察是否有异常,是否有内存泄漏:即cpu占用率是否一直在增长,内存在压测前后的变化
        3.关注中断是否发生变化，默认版本为330，若要修改，需修改脚本中的中断值，并跟换EVB板，也可使用auto EVB板
3、使用未占满cpu版本调试
4、版本RN：
	v1.0：首次编写
'''

import serial
import time
import os
import sys
import random
import re
import subprocess
import threading


# 定义常量
DEBUG_PORT = "COM20"
AT_PORT = "COM41"
BAUDRATE = 115200
# 定义变量
at_serial = None
debug_serial = None
#记录运行次数
runTimes		= 0
#log文件
DEBUG_LOG = ""
ADB_LOG = ""
AT_LOG = ""

# 创建日志文件
def createLogFile():
    global DEBUG_LOG, ADB_LOG, AT_LOG
    timestamp = time.strftime("%Y-%m-%d_%H_%M_%S", time.localtime())
    DEBUG_LOG = f"DEBUG_LOG_{timestamp}.log"
    ADB_LOG = f"ADB_LOG_{timestamp}.log"
    AT_LOG = f"AT_LOG_{timestamp}.log"
    
    with open(DEBUG_LOG, 'w', encoding='utf-8') as file:
        file.write("DEBUG 日志开始记录...\n")
    
    with open(ADB_LOG, 'w', encoding='utf-8') as file:
        file.write("ADB 日志开始记录...\n")
        
    with open(AT_LOG, 'w', encoding='utf-8') as file:
        file.write("AT 日志开始记录...\n")
        
# 写日志
def write_log(log, logFile):
    with open(logFile, 'a', encoding='utf-8') as file_object:
        file_object.write(str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "\r\n" + log + "\r\n")
        now_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        print("[" + now_time+ "]" + " " + log)
#发送at指令
def at_write(command, timeout, DEBUG_PORT_opened, at_result,RDY=0,CPIN=0):
 write_log(str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())),DEBUG_LOG)
 at_command = command.encode("UTF-8")
 DEBUG_PORT_opened.write(at_command)

#读取at口返回
def at_read(serial_port, logFile=""):
 rec_str = ''
 while (True):
  time.sleep(1)
  lenth = serial_port.inWaiting()
  if lenth > 0:
   rec_str = serial_port.read(size=lenth)
   try:
    rec_str = re.sub('\\x1b.*?m', "", rec_str.decode("UTF-8"), 0, re.I)
    if logFile != "":
     with open(logFile, 'a') as file_object:
      rec_str = str(time.time()) + ":" + str(
       time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "\r\n" + rec_str
      file_object.write(
       str(time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())) + "\r\n" + rec_str + "\r\n")
    return rec_str
   except:
    return ""
  else:
   break
 return rec_str

# 登录设备
def loginDevice():
 global DEBUG_LOG
 #确认登录失败的条件，或者，不可能失败
 while True:
  print("等待登录... ... ")
  time.sleep(3)
  at_write("\r\n",3,debug_serial,"OK")
  result = at_read(debug_serial)

  with open(DEBUG_LOG, 'a') as file_object:
   file_object.write(result)

  if "login:" in result:
   at_write("root\r\n",3,debug_serial,"OK")
   print("账号输入成功")
   time.sleep(2)
   at_write("oelinux123\r\n",3,debug_serial,"OK")
   print("密码输入成功")
  elif "~ #" in result:
   print("登录成功")
   break
  else:
   continue 

#检查debug口通信状态
def check_port_status():
 global debug_serial
 if debug_serial == None:
  try:
   debug_serial = serial.Serial(DEBUG_PORT,BAUDRATE)
  except:
   print("端口连接失败，请检查端口")
   sys.exit()

# 写入运行次数到日志
def writeRuntimesToLog():
 global runTimes
 with open(DEBUG_LOG, 'a+') as file_object:
  file_object.write("\r\n{} runtimes: {} {}\r\n".format("<"*20,runTimes,">"*20))
 print("\r\n{} runtimes: {} {}\r\n".format("<"*20,runTimes,">"*20))
 runTimes += 1
 
#发送AT指令
def send_at_command(command):
        # 打开串口
        ser = serial.Serial(AT_PORT, BAUDRATE)
        ser.write((command + "\r\n").encode())
        ser.write(("\r\n").encode())
        time.sleep(6)
        response_qmssr = ser.read_all().decode()
        ser.close()
        print(response_qmssr)
        write_log(f"at指令{command}执行结果:{response_qmssr}",AT_LOG)
        
#发送adb命令          
def send_adb_command(command):
    try:
        # 使用 subprocess 模块执行 adb shell 命令
        result = subprocess.run(['adb', 'shell', command], capture_output=True, text=True)
        # 返回命令的输出结果
        return result.stdout
    except Exception as e:
        return str(e)
   
def get_top_cpu_idle_process():
    
    result = send_adb_command('top -n 1 | grep idle')
    try:
        cpu_idle = result.split("nic ")[1].split("% idle")[0]
        cpu_usage = 100 - float(cpu_idle)  # 计算 CPU 占用率
        # write_log(f"CPU占用率: {cpu_usage}", ADB_LOG)
        return cpu_usage  # 返回 CPU 占用率
    except IndexError:
        write_log("解析CPU占用率信息时出错")
        return None

def get_monitor_cpu_usage():
    write_log("开始获取CPU占用率", ADB_LOG)
    cpu_usages = []  # 用于存储窗口内的CPU占用率
    flag = 0  # 用于计数，每隔1秒采集一次CPU占用率
    while True:
        cpu_idle = get_top_cpu_idle_process()
        if cpu_idle is not None:
            cpu_usage = cpu_idle  # 计算CPU占用率
            cpu_usages.append(cpu_usage)
        time.sleep(1)
        flag += 1
        if flag == 10:
            break  
# 计算平均CPU占用率并保留两位小数
    cpu_avg = round(sum(cpu_usages) / len(cpu_usages), 2)
    
    # 记录日志
    # write_log(f"CPU占用率: {cpu_usages}", ADB_LOG)
    write_log(f"10S内的平均CPU占用率: {cpu_avg}", ADB_LOG)
    return cpu_avg
        
def get_mem_process():
    write_log("开始获取MemAvailable内存信息", ADB_LOG)
    mem_info = {}
    result = send_adb_command('cat /proc/meminfo\n')
    try:
        for line in result.strip().split('\n'):
            if line.startswith('Mem'):
                key, value = re.split(r':\s+', line)
                mem_info[key] = int(value.split()[0])
        return mem_info.get('MemAvailable', None)  # 返回 MemAvailable 的值
    except IndexError:
        write_log("返回mem信息时出错，解析内存信息时出错",ADB_LOG)
        return None

def get_monitor_memory():
    declining = True
    current_mem_available = get_mem_process()
    write_log(f"MemAvailable: {current_mem_available} kB",ADB_LOG)
    time.sleep(2)
    return current_mem_available
    
            
#执行./sample_sensor 查看Sensor数据输出
def exe_sample_sensor():
    write_log("开始执行./sample_sensor", DEBUG_LOG)
    at_write("cd / \r\n",3,debug_serial,"OK")
    at_write("./data/sample_sensor\r\n", 3, debug_serial, "OK")
    time.sleep(10)
    result = at_read(debug_serial, DEBUG_LOG)
    # print(f"./sample_sensor执行结果:{result}")
    return result       
def get_interrupted():
    write_log("开始获取中断信息", DEBUG_LOG)
    at_write("cat /proc/interrupts  |grep 330\r\n",3,debug_serial,"OK")
    time.sleep(3)
    result = at_read(debug_serial,DEBUG_LOG)
    return result

def get_adb_interrupted():
    write_log("开始获取中断信息", ADB_LOG)
    result = send_adb_command('cat /proc/interrupts  |grep 330')
    if result:
        write_log(f"中断信息:\n{result}", ADB_LOG)
    # 正则表达式匹配时间戳
    pattern = r"(\d+):\s+(\d+)\s+"
    match = re.search(pattern,result )
    if match:
        timestamp = match.group(2)  # 提取第二个捕获组，即 
        print("提取的中断信息:", timestamp)
    else:
        print("未提取到中断信息")
    return timestamp  # 返回中断信息
    

def get_ps_gps():
    write_log("开始获取GPS进程信息", DEBUG_LOG)
    at_write("ps|grep gps\r\n",3,debug_serial,"OK")
    time.sleep(3)
    result = at_read(debug_serial,DEBUG_LOG)
    time.sleep(2)
    return result

def check_mem_declining(mem_usages):
    write_log(f"mem数据: {mem_usages}", ADB_LOG)
    
    usages = mem_usages
    if len(mem_usages) <= 1:
        write_log("mem数据不足，无法判断", ADB_LOG)
        return
    write_log(f"压测开始第一位内存数据:{mem_usages[0]}kB", ADB_LOG)
    write_log(f"压测当前最后一位内存数据:{mem_usages[-1]}kB", ADB_LOG)
    #计算前后两位内存数据差值
    diff = abs(mem_usages[-1] - mem_usages[0])
    write_log(f"压测前后两位内存数据差值:{diff}KB", ADB_LOG)
    mem_declining_threshold = 5*1024  # 内存差值警戒值
    if diff >mem_declining_threshold:
        write_log("差值超过5MB，可能存在内存泄漏", ADB_LOG)
    else:
        write_log("当前差值未超过5MB", ADB_LOG)
    is_increasing = all(usages[i] > usages[i + 1] for i in range(len(usages) - 1))
    if is_increasing:
        write_log("mem持续下降", ADB_LOG)
    else:
        write_log("mem没有持续下降", ADB_LOG)
        

def check_cpu_declining(cpu_usages):
    usages = cpu_usages
    write_log(f"cpu数据: {cpu_usages}", ADB_LOG)
    if len(cpu_usages) <= 1:
        write_log("cpu数据不足，无法判断", ADB_LOG)
        return
    is_increasing = all(usages[i] < usages[i + 1] for i in range(len(usages) - 1))
    if is_increasing:
        write_log("cpu占用率持续上升", ADB_LOG)
    else:
        write_log("cpu占用率没有持续上升", ADB_LOG)
        
def check_inter_info(inter_infos):
    write_log(f"中断信息: {inter_infos}", ADB_LOG)
    if len(inter_infos) <= 1:
        write_log("中断信息不足，无法判断", ADB_LOG)
        return
    for i in range(len(inter_infos) - 1):
        if inter_infos [i] != inter_infos[i+1]:
            write_log(f"中断信息发生变化", ADB_LOG)
            return True
        else:
            write_log(f"中断信息没有发生变化", ADB_LOG)
            return False
        
def extract_sensor_data(result):
    lines = result.strip().split('\n')
    sensor_data = []
    
    for line in lines:
        fields = line.split(',')
        
        # 检查字段数量和类型
        if len(fields) >= 7 and fields[3] in ["ACC", "GYU", "TEMP"]:
            try:
                x = float(fields[4].split('=')[1])
                y = float(fields[5].split('=')[1])
                z = float(fields[6].split('=')[1])
                sensor_data.append(line)  # 有效数据
            except (ValueError, IndexError):
                continue  # 跳过无效行
    
    return sensor_data

# 检查传感器数据的有效性
def check_sensor_data(data):
    for line in data:
        fields = line.split(',')
        
        # 提取数据
        index = fields[0]
        status = fields[1]
        timestamp = fields[2]
        sensor_type = fields[3]
        x = float(fields[4].split('=')[1])
        y = float(fields[5].split('=')[1])
        z = float(fields[6].split('=')[1])
        
        # 校验 TEMP 数据
        if sensor_type == "TEMP":
            if y != 0 or z != 0:
                # print(f"数据错误: {line} -> TEMP 传感器的 y 和 z 应为 0")
                write_log(f"数据错误: {line} -> TEMP 传感器的 y 和 z 应为 0", ADB_LOG)
                return False
            else:
                # print(f"TEMP 数据正常: {line}")
                write_log(f"TEMP 数据正常: {line}", ADB_LOG)
        
        # 校验 ACC 和 GYU 数据 (不全为 0 即可)
        elif sensor_type in ["ACC", "GYU"]:
            if x == 0 and y == 0 and z == 0:
                # print(f"数据错误: {line} -> {sensor_type} 传感器的 x, y, z 全为 0")
                write_log(f"数据错误: {line} -> {sensor_type} 传感器的 x, y, z 全为 0", ADB_LOG)
                return False    
            else:
                # print(f"{sensor_type} 数据正常: {line}")
                write_log(f"{sensor_type} 数据正常: {line}", ADB_LOG)
        
        else:
            # print(f"未知传感器类型: {sensor_type}")
            write_log(f"未知传感器类型: {sensor_type}", ADB_LOG)
    return True  # 所有校验正常时返回 True

#验证QDR功能是否正常开启
def check_qdr_functionality(qdr_statu):
    qdr_status = qdr_statu 
    if qdr_status:
        write_log("QDR 功能正常开启", ADB_LOG)
        return True
    else:
        write_log(f"QDR 功能未正常开启", ADB_LOG)
        return False
        
def check_gps_processes(process_output):
    # 正则表达式匹配 GPS 相关进程
    pattern = re.compile(r'^\s*\d+\s+\w+\s+\d+:\d+\s+.*(loc_launcher|location_hal_daemon)')
    # 提取所有有效的进程行
    gps_processes = [line for line in process_output.splitlines() if pattern.search(line)]
    print(gps_processes)
    # 检查是否有相关进程
    gps_running = bool(gps_processes)
    write_log(f"GPS 进程: {gps_processes}", ADB_LOG)
    if gps_running:
        # print("GPS 服务正在运行:")
        write_log("GPS 服务正在运行:", ADB_LOG)
        for process in gps_processes:
            print(process)
    else:
        # print("GPS 服务未运行或异常.")
        write_log("GPS 服务未运行或异常.", ADB_LOG)
                
def get_sensor_log_via_adb():
    timeout=5
    command = "adb logcat -s slim_daemon"
    process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

    output_lines = []
    start_time = time.time()

    try:
        while True:
            output = process.stdout.readline()
            if output == b"" and process.poll() is not None:
                break
            if output:
                line = output.decode().strip()
                # 打印所有输出以调试
                print(line)  # 输出每一行，检查是否有数据
                output_lines.append(line)

            # 检查是否超时
            if time.time() - start_time > timeout:
                break

        # 停止进程
        process.terminate()

        return "\n".join(output_lines)

    except Exception as e:
        print(f"异常: {e}")
        process.terminate()
def adb_root():
    try:
        # 执行 adb root
        subprocess.run(["adb", "root"], check=True)
        print("已发送 adb root 命令")

        # 确认是否获得 root 权限
        result = subprocess.run(["adb", "shell", "whoami"], capture_output=True, text=True)
        if result.stdout.strip() == "root":
            print("成功获得 root 权限")
        else:
            print("未获得 root 权限，请检查设备设置")
    
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
        
def pid_manage_process():
    try:
        # Step 1: 获取 root 权限
        subprocess.run(["adb", "root"], check=True)
        print("已发送 adb root 命令")
        # 检查是否获得 root 权限
        result = subprocess.run(["adb", "shell", "whoami"], capture_output=True, text=True)
        if result.stdout.strip() != "root":
            print("未获得 root 权限，请检查设备设置")
            return
        # Step 2: 查找进程 ID
        result = subprocess.run(['adb', 'shell', 'ps -ef | grep sample_sensor'], capture_output=True, text=True)
        output = result.stdout
        print("查找进程输出:", output)
        # 使用正则表达式查找以./sample_sensor开头的行
        match = re.search(r"(\d+)\s+root\s+\S+\s+\./sample_sensor", output)
        if not match:
            print("未找到进程 ./sample_sensor")
            return
        pid = match.group(1)
        # print(f"进程ID号: {pid}")
        # Step 3: 杀死进程
        kill_result = subprocess.run(['adb', 'shell', f'kill -9 {pid}'], capture_output=True, text=True)
        if kill_result.returncode == 0:
            print(f"成功杀死进程 {pid}")
            print("关闭sample_sensor成功")
        else:
            print(f"无法杀死进程 {pid}：{kill_result.stderr}")
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
def main():
    mme_usages = []
    cpu_usages = []
    inter_infos= []
    # 检查串口连接状态，确保 debug_serial 已连接
    check_port_status() 
    createLogFile()  # 确保日志文件被创建
    # 打印日志路径，确认日志文件已创建
    print(f"DEBUG_LOG: {DEBUG_LOG}, ADB_LOG: {ADB_LOG}, AT_LOG: {AT_LOG}")
    # 检查串口连接状态，确保 debug_serial 已连接
    check_port_status() 
    # 登录设备
    loginDevice()
    #查看GPS进程
    gps_data = get_ps_gps()
    check_gps_processes(gps_data)
    # 查看230中断
    get_interrupted()
    #开关./sample_sensor 查看Sensor数据输出，开关5-10次
    for i in range(5):
        exe_sample_sensor()
        debug_data =at_read(debug_serial, DEBUG_LOG)
        print(f"Sensor data output: {debug_data}") # 检查输出
        time.sleep(2)
        sensor_data = extract_sensor_data(debug_data)
        time.sleep(2)
        print(f"Extracted sensor data: {sensor_data}") # 检查提取的数据
        #校验传感器数据
        qdr_state = check_sensor_data(sensor_data)
        pid_manage_process()
    
    # 执行./sample_sensor 查看Sensor数据输出
    exe_sample_sensor()
    # 循环执行测试
    while True:
        #记录脚本运行次数
        writeRuntimesToLog()
        time.sleep(3)
        #获取debug口数据
        # 执行./sample_sensor 查看Sensor数据输出
        exe_sample_sensor()
        debug_data =at_read(debug_serial, DEBUG_LOG)
        print(f"Sensor data output: {debug_data}") # 检查输出
        time.sleep(2)
        sensor_data = extract_sensor_data(debug_data)
        time.sleep(2)
        print(f"Extracted sensor data: {sensor_data}") # 检查提取的数据
        #校验传感器数据
        qdr_state = check_sensor_data(sensor_data)
        # 验证QDR功能是否正常开启
        check_qdr_functionality(qdr_state)
        # 查看cpu占用不会持续升高
        cpu_usage = get_monitor_cpu_usage()
        cpu_usages.append(cpu_usage)
        check_cpu_declining(cpu_usages)#检查cpu占用不会持续升高
        #查看mem占用不会持续下降
        mem_usage = get_monitor_memory()
        mme_usages.append(mem_usage)
        check_mem_declining(mme_usages)#检查mem占用不会持续下降
        #开始获取中断信息
        inter_info = get_adb_interrupted()
        inter_infos.append(inter_info)
        check_inter_info(inter_infos)#检查中断信息是否发生变化
        time.sleep(3)
        
    return cpu_usages, mme_usages

# 主程序
if __name__ == '__main__':
    main()
    




















